
#ifndef _EL_ENV_
#define _EL_ENV_

/*
 * This class contains all the things needed to run lisp. One el_env is equal
 * to one execution environment with separate memory/procs/data/everything.
 */

#include "type.h"

#include <map>
using std::map;
#include <vector>
using std::vector;
#include <set>
using std::set;
#include <list>
using std::list;

class el_env;

typedef el_var* (*el_c_proc) (el_var*, el_env*);

typedef map<string, el_var*> variable_scope;

class el_scope
{
	el_var* code;
	variable_scope locals;
	int except_scope;
	el_var* except_symbol;
	el_var* except_code;
};

class el_heap_entry
{
public:
	size_t start;
	size_t size;
	inline bool operator< (const el_heap_entry& a) const
	{
		return start < a.start;
	}
	inline bool operator== (const el_heap_entry& a) const
	{
		return start == a.start;
	}
	inline el_heap_entry()
	{
		start = 0;
		size = 0;
	}
	inline el_heap_entry (size_t st, size_t si)
	{
		start = st;
		size = si;
	}
};

class el_env
{

	vector<el_type> type;

	variable_scope globals;
	vector<el_scope> stack;

	// Memory nanagment (garbage collector)

	void* heap;
	size_t hs, align;

	/*
	 * NOTE. (quite important)
	 * align shouldn't be non-power of 2, but also should not be higher
	 * that sizeof(el_var) - this could lead to significant memory overhead.
	 */

	set<el_heap_entry> allocated_space;
	set<el_heap_entry> free_space;
	set<el_var*> collector;
	list<el_var*> collector_queue;

	void* new_heap_object (size_t size);

	void* allocate (size_t size);
	void deallocate (void*);

	el_var * new_var (int type, size_t data_size);
	void free_var(el_var*);
	void mark_collectable(el_var*);

	void sort_out_free_space();

public:
	el_env (size_t heap_size = 65536, size_t alignment = 4);
	~el_env();

	int register_type (const el_type& t);
	int register_c_proc (const string& name, el_c_proc);

	el_var* eval (el_var*);

	void collect_garbage ();
};

#endif

